Skip to content

feat(report): SUMMARY.html — the one page that actually gets forwarded - #10

Merged
MyAlterLego merged 1 commit into
mainfrom
feat/summary-report
Aug 19, 2026
Merged

MyAlterLego merged 1 commit into
mainfrom
feat/summary-report

Conversation

@MyAlterLego

Copy link
Copy Markdown
Contributor

What

stpa summary writes a one-page SUMMARY.html beside REPORT.html. stpa run now emits both.

REPORT.html is written for the engineers who will fix the findings — ten sections, the full grid, every scenario. Nobody forwards it to a founder. The analysis reached the people who could fix things and stopped at the people who decide whether it gets fixed.

The summary is one sheet: stat row, what is at stake, wave 1, top root causes by leverage. Self-contained, no JavaScript, prints clean, light/dark aware.

The design constraint

A summary is the artifact most likely to be read instead of the report, which makes it the artifact most likely to launder an incomplete analysis into a confident one — the exact failure class this toolkit exists to find.

So every qualifier the report carries, this page carries too, above the numbers rather than in a footnote: not peer-reviewed, sections missing, scope not delivered, cells open, findings unbound. CI asserts the unreviewed banner is present, because that is the one a hurried author would be most tempted to drop.

Both pages render from the same artifacts, so they cannot drift. Every figure is computed; none is narrated.

Two fixes found while building it

  • Losses parsed out of 01-scope.md rendered L-1 — — the loss. Stripping the id from the bold half leaves the dash on the text half.
  • The hazard reach column read only linksTo, which no finding in the worked example records — so it showed 0 beside findings whose own statements say "leading to H-3". That is not a missing number, it is a wrong one. It now falls back to the hazard ids the statements name, and the table states which source produced the column. RenderReport.ts §4 still has the original behaviour — same six lines, same all-zero column. Left alone here to keep this PR to one change; worth a follow-up.

Verification

  • Full CI smoke suite passes locally, 6/6, plus the new summary step.
  • New CI step asserts: renders, non-empty, no external src/href, no <script>, carries the NOT-INDEPENDENTLY-REVIEWED banner, links REPORT.html.
  • Negative case: exits 1 with a clean message when there is no grid.json.
  • Rendered page verified in real Chrome — header, banner, 5-stat row, losses, hazard table, wave-1 cards, root causes, footer link all correct.
  • SUMMARY.html for the worked example is committed alongside the existing REPORT.html.

Not in this PR

stpa run and every Tools/*.ts are broken under Node on Node 25.9.0 — SyntaxError: Cannot use import statement outside a module. The tools are ESM, the repo has no package.json, so Node resolves .ts as CJS; adding {"type":"module"} fixes the tools but breaks the CJS stpa launcher. CI only installs Bun and never invokes ./stpa <subcommand>, which is why #9's "runs on Node or Bun" shipped green. Filed separately.

🤖 Generated with Claude Code

REPORT.html is written for the engineers who will fix the findings. It is ten
sections long because the method is exhaustive, and nobody forwards it to a
founder. So the analysis reached the people who could fix it and stopped at the
people who decide whether it gets fixed.

`stpa summary` (and `stpa run`, which now emits both) writes a one-page
SUMMARY.html beside the report: the stat row, what is at stake, wave 1, and the
top root causes by leverage. Self-contained, no JavaScript, prints to a sheet.

The design constraint that shaped it: a summary is the artifact most likely to
be read INSTEAD of the report, which makes it the artifact most likely to
launder an incomplete analysis into a confident one — the exact failure class
this toolkit exists to find. So every qualifier the report carries, this page
carries too, ABOVE the numbers rather than in a footnote: not peer-reviewed,
sections missing, scope not delivered, cells open, findings unbound. CI asserts
the unreviewed banner is present, because that is the one a hurried author would
be most tempted to drop.

Both pages render from the same artifacts, so they cannot drift. Every figure is
computed; none is narrated.

Two details worth naming:

- Losses parsed out of 01-scope.md render "L-1 — the loss", not "L-1 — — the
  loss": stripping the id from the bold half leaves the dash on the text half.
- The hazard reach column prefers recorded `linksTo`, but falls back to the
  hazard ids the finding statements themselves name when no finding records a
  link. A column of zeros beside findings that say "leading to H-3" is not a
  missing number, it is a wrong one. The table states which source produced it.

The summary never gates: if it fails to render, the report that already rendered
is unaffected and `stpa run` says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MyAlterLego
MyAlterLego merged commit fdc1588 into main Aug 19, 2026
2 checks passed
@MyAlterLego
MyAlterLego deleted the feat/summary-report branch August 19, 2026 01:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant